#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <locale.h>
#include "mls.h"

typedef unsigned char uchar;
#define MAX_LENGTH 10

struct testcase {
    const char *utf8_str;
    size_t len;
    uint32_t u[MAX_LENGTH];
    int invalid_ucs2;
};

void m_dump_char(int m);
void dump_hex( const char *s);
void dump_wide( const char *s);
int read_file( char *fn );
int m_wctomb(int buf,  wchar_t wc );
void wc_to_utf16(int buf, wchar_t wc);
void write_utf16( wchar_t wc, FILE *fp);
void print_testcase( const struct testcase *t );

static const struct testcase testcases[] = {
    {"", 0, {0}},
    {"\x01", 1, {1}},
    {"\x7F", 1, {0x7F}},
    {"\x01\x7F", 2, {0x01, 0x7F}},
    {"\xC0\x80", 1, {0}},
    {"\xC0\x81", 1, {1}},
    {"\xC1\x80", 1, {0x40}},
    {"\xDF\xBF", 1, {0x7FF}},
    {"\xE0\x80\x80", 1, {0}},
    {"\xE0\x80\x81", 1, {1}},
    {"\xE0\x81\x80", 1, {0x40}},
    {"\xE1\x80\x80", 1, {0x1000}},
    {"\xEF\xBF\xBF", 1, {0xFFFF}},
    {"\xF0\x80\x80\x80", 1, {0}},
    {"\xF0\x80\x80\x81", 1, {1}},
    {"\xF0\x80\x81\x80", 1, {0x40}},
    {"\xF0\x81\x80\x80", 1, {0x1000}},
    {"\xF1\x80\x80\x80", 1, {0x40000}},
    {"\xF7\xBF\xBF\xBF", 1, {0X1FFFFF}, 1},
};


int main()
{
    int i, val,p;
    char *s;
    uint32_t wc;
    char t2[] = { 0xed, 0x9f, 0xbf, 0 };
    char *utf8_test = "κόσμε";

    setlocale(LC_ALL,"");
    m_init();

    printf("test case: ");
    dump_hex(t2);
    putchar(10);
    s=t2; wc = utf8char(&s);
    printf( "utf8 to wc: %x\nwc to utf8: ", wc );

    int buf = m_wctomb( 0, wc ); /* buf is a safe-array */
    m_dump_char(buf);
    putchar(10);
    m_free(buf); /* free memory, destroy safe-array */

    int b = read_file( "UTF-8-test.txt" );
    printf("read: %d\n", m_len(b));

    FILE *fp = fopen("test-utf16","w");
    write_utf16( 0xFEFF, fp ); /* Byte order mark (BOM) */

    p=0;
    while( (val=m_utf8char(b,&p)) != -1 )
        write_utf16( val, fp );

    fclose(fp);
    m_free(b);

    for (i = 0; i < sizeof(testcases)/sizeof(testcases[0]); ++i) {
	const struct testcase *t = &testcases[i];
	printf("%d. ",i );
        dump_wide( t->utf8_str );
        printf(" == ");
        print_testcase(t);
        putchar(10);
    }

    printf("testcase: %s\nutf8: ", utf8_test); dump_hex (utf8_test); putchar(10);
    printf("wide: "); dump_wide(utf8_test);
    putchar(10);

    m_destruct();
    return 0;
}

void m_dump_char(int m)
{
    int i;
    unsigned char *ch;
    m_foreach(m,i,ch)
        printf("%x ", (unsigned)*ch );
}

void dump_hex( const char *s)
{
    while(*s) {
      printf("%02x ", *(uchar*)s );
	s++;
    }
}


void dump_wide( const char *s)
{
    int ret;
    while( (ret=utf8char( (char**) &s)) >=0 ) {
	printf("%0x ", ret );
    }
}

int read_file( char *fn )
{
  int ch;
  FILE *fp = fopen(fn, "r" );
  if( !fp ) return -1;
  int m = m_create(100,1);
  while( !feof(fp)  ) {
    ch=fgetc(fp);
    m_putc(m,ch);
  }
  return m;
}

int m_wctomb(int buf,  wchar_t wc )
{
  if( buf > 1 )  m_clear(buf); else buf=m_create(MB_CUR_MAX,1);
  int len = wctomb( m_buf(buf), wc );
  m_setlen(buf,len);
  return buf;
}

/** wide character (wc) to utf-16 encoding
    wc <=  0xffff --> wc
    wc -= 0x10000
    0b110110 b19 b18  b17-b10
    0b110111 b9  b8   b7-b0
*/
void wc_to_utf16(int buf, wchar_t wc)
{
    uint16_t c16;
    m_clear(buf);
    if( wc >= 0x10ffff ) wc=0xfffd;
    if( wc < 0x10000 ) {
        c16 = wc; m_write(buf,0,&c16,2);
        return;
    }
    wc -= 0x10000;
    c16 = 0xd800 | (wc >> 10);
    m_write(buf,0,&c16,2);
    c16 = 0xdc00 | (wc & 0x3ff);
    m_write(buf,0,&c16,2);
}

void write_utf16( wchar_t wc, FILE *fp)
{
    static int buf = 0;
    if( !buf ) buf = m_create( 4, sizeof(char) );
    wc_to_utf16(buf,wc);
    fwrite( m_buf(buf), m_len(buf), 1, fp );
}

void print_testcase( const struct testcase *t )
{
  int i;
  for(i=0;i< t->len; i++)
    printf( "%0x ", t->u[i] );
}
